package com.lingyue.commons.encrypt;


/**
 * Created by IntelliJ IDEA.
 *
 * @Description: TODO
 * @Author: BaiJinhua
 * @Date: 2019/11/28 17:04
 * @See: com.king.token.tools
 * @Version: 1.0
 * Copyright: Copyright (c) 2019
 */

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;

public class AesEncryptUtils {

    /**
     * 可配置到Constant中，并读取配置文件注入,16位,自己定义
     */
    private static final String KEY = "g&harOP[AW9d3\"pp";


    public static boolean initialized = false;


    private static final String AES_ALG = "AES";


    /**
     * 参数分别代表 算法名称/加密模式/数据填充方式
     */
    private static final String AES_CBC_PCK_ALG = "AES/CBC/PKCS7Padding";

    /**
     * 默认偏移量
     */
    private static final byte[] AES_IV = initIv();

    private static final String CHARSET = "UTF-8";

    /**
     * 加密
     *
     * @param content    加密的字符串
     * @param encryptKey key值
     * @return
     * @throws Exception
     */
    public static String encrypt(String content, String encryptKey) throws Exception {
        initialize();
        Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);
        IvParameterSpec iv = new IvParameterSpec(AES_IV);
        SecretKeySpec spec = new SecretKeySpec(init(encryptKey), AES_ALG);
        cipher.init(Cipher.ENCRYPT_MODE, spec, iv);
        byte[] encryptBytes = cipher.doFinal(content.getBytes(CHARSET));
        byte[] encryptNew = new byte[AES_IV.length+encryptBytes.length];
        System.arraycopy(AES_IV, 0, encryptNew, 0, AES_IV.length);
        System.arraycopy(encryptBytes, 0, encryptNew, AES_IV.length, encryptBytes.length);
        return new String(Base64.getMimeEncoder().encode(encryptNew));
    }

    public static String encrypt(byte[] content, String encryptKey) throws Exception {
        initialize();
        Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);
        IvParameterSpec iv = new IvParameterSpec(AES_IV);
        SecretKeySpec spec = new SecretKeySpec(init(encryptKey), AES_ALG);
        cipher.init(Cipher.ENCRYPT_MODE, spec, iv);
        byte[] encryptBytes = cipher.doFinal(content);
        byte[] encryptNew = new byte[AES_IV.length+encryptBytes.length];
        System.arraycopy(AES_IV, 0, encryptNew, 0, AES_IV.length);
        System.arraycopy(encryptBytes, 0, encryptNew, AES_IV.length, encryptBytes.length);
        return new String(Base64.getMimeEncoder().encode(encryptNew));
    }

    /**
     * 解密
     *
     * @param cipherText 解密的字符串
     * @param decryptKey 解密的key值
     * @return
     * @throws Exception
     */
    public static String decrypt(String cipherText, String decryptKey) throws Exception {
        initialize();
        Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);
        SecretKeySpec spec = new SecretKeySpec(init(decryptKey), AES_ALG);
        byte[] decode = Base64.getMimeDecoder().decode(cipherText);
        byte[] ivByte = new byte[AES_IV.length];
        System.arraycopy(decode, 0, ivByte, 0, AES_IV.length);
        IvParameterSpec iv = new IvParameterSpec(ivByte);
        cipher.init(Cipher.DECRYPT_MODE, spec, iv);
        byte[] cipherByte = new byte[decode.length-AES_IV.length];
        System.arraycopy(decode, AES_IV.length, cipherByte, 0, decode.length-AES_IV.length);
        return new String(cipher.doFinal(cipherByte), CHARSET);
    }


    public static byte[] decryptByte(String cipherText, String decryptKey) throws Exception {
        initialize();
        Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);
        SecretKeySpec spec = new SecretKeySpec(init(decryptKey), AES_ALG);
        byte[] decode = Base64.getMimeDecoder().decode(cipherText);
        byte[] ivByte = new byte[AES_IV.length];
        System.arraycopy(decode, 0, ivByte, 0, AES_IV.length);
        IvParameterSpec iv = new IvParameterSpec(ivByte);
        cipher.init(Cipher.DECRYPT_MODE, spec, iv);
        byte[] cipherByte = new byte[decode.length-AES_IV.length];
        System.arraycopy(decode, AES_IV.length, cipherByte, 0, decode.length-AES_IV.length);
        return cipher.doFinal(cipherByte);
    }


    private static byte[] initIv() {
        byte[] iv;
        int blockSize = 16;
        iv = new byte[blockSize];
        for (int i = 0; i < blockSize; ++i) {
            iv[i] = 0x30;
        }
        return iv;
    }

    /**
     * 补密码长度
     * @param key
     * @return
     */
    public static byte[] init(String key) {
        byte[] keyBytes = key.getBytes();
        // 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
        int base = 16;
        if (keyBytes.length % base != 0) {
            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
            keyBytes = temp;
        }
        return keyBytes;
    }

    public static void initialize() {
        if (!initialized) {
            Security.addProvider(new BouncyCastleProvider());
            initialized = true;
        }
    }

    public static String getSecretKey(String seed) throws NoSuchAlgorithmException {
        KeyGenerator generator = KeyGenerator.getInstance(AES_ALG);
        SecureRandom securerandom = new SecureRandom(seed.getBytes());
        generator.init(128, securerandom);
        SecretKey secretKey = generator.generateKey();
        return new String(Base64.getMimeEncoder().encode(secretKey.getEncoded()));
    }

    public static SecretKeySpec getSecretKeySpec(String seed) throws NoSuchAlgorithmException {
        KeyGenerator generator = KeyGenerator.getInstance(AES_ALG);
        SecureRandom securerandom = new SecureRandom(seed.getBytes());
        generator.init(128, securerandom);
        SecretKey secretKey = generator.generateKey();
        SecretKeySpec keySpec = new SecretKeySpec(secretKey.getEncoded(), AES_ALG);
        return keySpec;
    }


}
